package com.bos.dao.base.imp;

import com.bos.dao.base.IBaseDao;
import com.bos.utils.PageBean;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.internal.CriteriaImpl;
import org.springframework.orm.hibernate5.support.HibernateDaoSupport;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

/**
 * @author Administrator
 * @Description 持久层通用接口实现类
 */
public class BaseDaoImp<T> extends HibernateDaoSupport implements IBaseDao<T> {

    private Class<T> entityClass;

    /*通过构造函数动态获取实体类的字节码*/
    public BaseDaoImp(){
        ParameterizedType superclass = (ParameterizedType) this.getClass().getGenericSuperclass();
        Type[] actualTypeArguments = superclass.getActualTypeArguments();
        entityClass= (Class<T>) actualTypeArguments[0];
    }

    /*注入sessionFactory*/
    @Resource(name = "sessionFactory")
    public void setMySessionFactory(SessionFactory sessionFactory){
        super.setSessionFactory(sessionFactory);
    }

    @Override
    public void save(T entity) {
        getHibernateTemplate().save(entity);
    }

    @Override
    public void update(T entity) {
        getHibernateTemplate().update(entity);
    }

    @Override
    public void delete(T entity) {
        getHibernateTemplate().delete(entity);
    }

    @Override
    public T findById(Serializable id) {
        return getHibernateTemplate().get(entityClass,id);
    }

    @Override
    public List<T> findAll() {
        String hql="from "+entityClass.getSimpleName();
        return (List<T>) getHibernateTemplate().find(hql);
    }

    @Override
    public void executeUpdate(String queryName, Object... objects) {
        Session currentSession = this.getSessionFactory().getCurrentSession();
        Query namedQuery = currentSession.getNamedQuery(queryName);
        int i = 0;
        for (Object object : objects) {
            //为HQL语句中的？赋值
            namedQuery.setParameter(i++, object);
        }
        namedQuery.executeUpdate();
    }

    @Override
    public void pageQuery(PageBean page) {




        int currentPage = page.getCurrentPage();
        int pageSize = page.getPageSize();
        DetachedCriteria detachedCriteria = page.getDetachedCriteria();


        //TODO controller单例导致的分页查询错误问题，可能是detachedCriteria遗留了某些数据为清除
        /*由于controller设置多例在这里可能detachedCriteria遗留了某些信息，我跟踪源码发现
         * queryParameter的firstRow,maxrows处于异常,还有executableCriteria的maxResults和firstResult
         * 现在的处理办法就是反射每次都重置其值或者让controller多例*/

        CriteriaImpl executableCriteria = (CriteriaImpl) detachedCriteria.getExecutableCriteria(getSessionFactory().getCurrentSession());
        Class<? extends CriteriaImpl> criteriaClass = executableCriteria.getClass();
        Field[] declaredFields = criteriaClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            declaredField.setAccessible(true);
            String name = declaredField.getName();
            if (name.equals("maxResults") || name.equals("firstResult")) {
                try {
                    declaredField.set(executableCriteria,null);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }


        //用hibernate中封装的方法不用谢hql或者sql语句即可查询数据总量total
        detachedCriteria.setProjection(Projections.rowCount());
        List<Long> total = (List<Long>) this.getHibernateTemplate().findByCriteria(detachedCriteria);
        page.setTotal(total.get(0).intValue());

        //查询该页面的数据
        detachedCriteria.setProjection(null);
        //设置关联映射转换器，如何不设置hibernate不会把数据封装在一个整体中，而是分开封装在各自的实体类里面
        detachedCriteria.setResultTransformer(DetachedCriteria.ROOT_ENTITY);
        int firstResult=(currentPage-1)*pageSize;
        int maxResults=pageSize;
        List<?> rows = this.getHibernateTemplate().findByCriteria(detachedCriteria,firstResult,maxResults);
        page.setRows(rows);
    }

    @Override
    public List<T> findByCriteria(DetachedCriteria detachedCriteria) {
        return (List<T>) this.getHibernateTemplate().findByCriteria(detachedCriteria);
    }
}
